namespace NBody.InitialConditions
{
    using System;
    
    public class Cube
    {
        public enum Origin { Corner, Centre };
        
        double side_length;
        double total_mass;
        Origin origin;
        
        public Cube(double side_length, double total_mass, Origin origin)
        {
            this.side_length = side_length;
            this.total_mass = total_mass;
            this.origin = origin;
        }
        
        public double SideLength
        {
            get { return side_length; }
        }
        
        public double TotalMass
        {
            get { return total_mass; }
        }
        
        public NBodySystem GenerateRandom(int n, double time, Random random)
        {
            NBodySystem system = new NBodySystem(n, time);
            double m = total_mass / (double)n;
            
            double offset = 0.0;
            if (origin == Origin.Centre)
                offset = 0.5 * side_length;
                
            for (int i = 0; i < n; i++)
            {
                double x = side_length * random.NextDouble() - offset;
                double y = side_length * random.NextDouble() - offset;
                double z = side_length * random.NextDouble() - offset;
             
                system.AddParticle(new Particle(m, x, y, z, 0.0, 0.0, 0.0));
            }
             
            return system;
        }
        
        public NBodySystem GenerateGrid(int n_per_side, double time)
        {
            int n = n_per_side * n_per_side * n_per_side;
            NBodySystem system = new NBodySystem(n, time);
            double m = total_mass / (double)n;
            double deltaX = side_length / (double)n_per_side;
            
            double offset = 0.0;
            if (origin == Origin.Centre)
                offset = 0.5 * side_length;
            
            for (int i = 0; i < n_per_side; i++)
            {
                for (int j = 0; j < n_per_side; j++)
                {
                    for (int k = 0; k < n_per_side; k++)
                    {
                        double x = deltaX * i - offset;
                        double y = deltaX * j - offset;
                        double z = deltaX * k - offset;
                    
                        system.AddParticle(new Particle(m, x, y, z, 0.0, 0.0, 0.0));
                    }
                }
            }
            
            return system;
        }
        
        public NBodySystem GenerateGrid(int n_per_side, double time, Displacements[] displacements)
        {
            int n = n_per_side * n_per_side * n_per_side;
            if (displacements.Length != n)
            {
                string message = String.Format("Length of displacements ({0}) do not equal number of particles ({1})", displacements.Length, n);
                throw new NBodyException(message);
            }
            
            NBodySystem system = new NBodySystem(n, time);
            double m = total_mass / (double)n;
            double deltaX = side_length / (double)n_per_side;
            
            double offset = 0.0;
            if (origin == Origin.Centre)
                offset = 0.5 * side_length;
            
            int count = 0;
            for (int i = 0; i < n_per_side; i++)
            {
                for (int j = 0; j < n_per_side; j++)
                {
                    for (int k = 0; k < n_per_side; k++)
                    {
                        double x = deltaX * i + displacements[count].Sx;
                        double y = deltaX * j + displacements[count].Sy;
                        double z = deltaX * k + displacements[count].Sz;
                        
                        while (x > side_length) x -= side_length;
                        while (x < 0.0) x += side_length;
                        while (y > side_length) y -= side_length;
                        while (y < 0.0) y += side_length;
                        while (z > side_length) z -= side_length;
                        while (z < 0.0) z += side_length;
                        
                        x -= offset;
                        y -= offset;
                        z -= offset;
                        
                        double vx = displacements[count].SVx;
                        double vy = displacements[count].SVy;
                        double vz = displacements[count].SVz;
                        
                        count++;
                    
                        system.AddParticle(new Particle(m, x, y, z, vx, vy, vz));
                    }
                }
            }
            
            return system;
        }            
    }
}
            
        
